/** by Emanuil Rusev | FXComponents.com */

package com.fxcomponents
{
	import com.fxcomponents.events.FXSoundEvent;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	
	[Event(name="playheadUpdate", type="com.fxcomponents.events.FXSoundEvent")]
	[Event(name="complete", type="com.fxcomponents.events.FXSoundEvent")]
	[Event(name="progress", type="flash.events.ProgressEvent")]
	[Event(name="progress", type="flash.events.ProgressEvent")]
	[Event(name="ioError", type="flash.events.IOErrorEvent")]
	[Event(name="open", type="flash.events.Event")]
	
	public class FXSound extends Sprite
	{
		public function FXSound()
		{
			super();
			
			sound = new Sound();
			sound.addEventListener(ProgressEvent.PROGRESS, onProgress);
			sound.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			sound.addEventListener(Event.OPEN, onOpen);
			
			soundTransform_ = new SoundTransform();
		}
		
		public var isPlaying:Boolean;
		
		private var sound:Sound;
		private var soundChannel:SoundChannel;
		private var soundTransform_:SoundTransform;
		
		private var _estimatedLength:Number;
		private var _playheadPosition:Number = 0;
		
		// Getters & Setteres
		
		public function get estimatedLength():Number
		{
			return _estimatedLength;
		}
		
		public function get playheadPosition():Number
		{
			return soundChannel.position;
		}
		
		public function set volume(value:uint):void
		{
			soundTransform_.volume = value/100;
			
			if(soundChannel)
			{
				soundChannel.soundTransform = soundTransform_;
			}
		}
		
		// Public Functions
		
		public function load(stream:URLRequest, context:SoundLoaderContext = null):void
		{
			sound.load(stream, context);
		}
		
		public function close():void
		{
			sound.close();
		}
		
		public function resume():Boolean
		{
			try
			{
				soundChannel = sound.play(_playheadPosition, 0, soundTransform_);
			}
			catch(error:ArgumentError)
			{
				trace("Error: FXSound -> resume() | " + error);
				return false;
			}
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
			
			isPlaying = true;
			
			return true;
		}
		
		public function pause():void
		{
			if(!soundChannel)
			{
				trace("Error: FXSound -> pause()");
				return;
			}
			
			_playheadPosition = soundChannel.position;
			soundChannel.stop();
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			isPlaying = false;
		}
		
		public function stop():void
		{
			if(!soundChannel)
			{
				trace("Error: FXSound -> stop()");
				return;
			}
			
			_playheadPosition = 0;
			soundChannel.stop();
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			isPlaying = false;
		}
		
		// Event Hanlders
		
		private function onSoundComplete(e:Event):void
		{
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			var event:FXSoundEvent = new FXSoundEvent(FXSoundEvent.COMPLETE);
			event.playheadTime = soundChannel.position;
			dispatchEvent(event);
		}
		
		private function onEnterFrame(e:Event):void
		{
			var event:FXSoundEvent = new FXSoundEvent(FXSoundEvent.PLAYHEAD_UPDATE);
			event.playheadTime = soundChannel.position;
			dispatchEvent(event);
		}
		
		private function onProgress(e:ProgressEvent):void
		{
			_estimatedLength = Math.ceil(sound.length/(sound.bytesLoaded/sound.bytesTotal));
			
			dispatchEvent(e.clone());
		}
		
		private function onIOError(e:IOErrorEvent):void
		{
			trace("IOError: " + e);
			
			dispatchEvent(e.clone());
		}
		
		private function onOpen(e:Event):void
		{
			dispatchEvent(e.clone());
		}
	}
}